''' 作业相关的处理程序 '''
from http.client import responses
from urllib import request

from jinja2 import TemplateNotFound
import tornado
from http.client import responses

from jinja2 import TemplateNotFound
import tornado
from tornado import web, gen
from tornado.httputil import url_concat
from tornado.options import options

import os
import shutil
import json
import logging
import pymysql
import time

from .base import BaseHandler
from utils.dbutil import MySQLConnection
from utils.jsonresult import JsonResult, json_result_encoder
from utils.pathutil import PathBuilder
from utils.dockerutil import JupyterServiceUtil

class AssignmentHandler(BaseHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        user = self.get_current_user()
        classid = self.get_argument('classid', 0)
        accountname = self.get_argument('accountname', '')
        labcode = self.get_argument('labcode', 0)
        
        if classid != 0:
            self.render('assignments.html', user=user, classid=classid)
        else:
            conn = MySQLConnection.get_connection()
            try:
                with conn.cursor() as cursor:
                    sql = "select labfile from lab where code='{}'".format(labcode)
                    cursor.execute(sql)
                    labfile = cursor.fetchone()['labfile']
            finally:
                conn.close()
            self.render('assignment.html', user=user, accountname=accountname, labcode=labcode, labfile=labfile)


    @web.authenticated
    @gen.coroutine
    def post(self):
        teacher_name = self.get_current_user()['accountname']
        request_data = json.loads(self.request.body.decode())
        operation = request_data.get('operation', '')
        operand = request_data.get('operand', '')

        json_result = JsonResult()

        call_func = dispatch_map.get(operation)
        if call_func is None:
            json_result.message = '无效的操作'
        else:
            call_func(teacher_name, operand, json_result)

        self.write(json.dumps(json_result, ensure_ascii=True, default=json_result_encoder))
        self.finish() 
    

    @staticmethod
    def get_class_assignments(teacher_name, operand, json_result):
        classid = operand.get('classid', 0)
        if classid == 0:
            json_result.success = False
            json_result.message = "错误的班级编号"
            return
        
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select code from course where courseid=(select courseid from class where classid={})".format(classid)
                cursor.execute(sql)
                course_code = cursor.fetchone()['code']

                sql = "select labid, code as labcode, labname, labfile from lab where LPAD(code, 2, 0)='{}'\
                    and labtype=2 and labfile is not null".format(course_code)
                cursor.execute(sql)
                course_assignments = cursor.fetchall()
                lab_codes = ["'" + item['labcode'] + "'" for item in course_assignments]                
                lab_codes = ",".join(lab_codes)

                sql = "select a.accountname, a.truename from account as a inner join classstudent as b on a.accountid=b.accountid\
                    inner join class as c on b.classid=c.classid where c.classid={}".format(classid)
                cursor.execute(sql)
                class_students = cursor.fetchall()
                accountnames = ["'" + item['accountname'] + "'" for item in class_students]
                accountnames = ",".join(accountnames)

                sql = "select assignmentid, labcode, accountname, (not isnull(reviewtime)) as reviewed \
                    from assignment where labcode in ({}) and accountname in ({})".format(lab_codes, accountnames)
                cursor.execute(sql)
                class_assignments = cursor.fetchall()

                json_result.data = {'course_assignments':course_assignments, 'class_students':class_students, 'class_assignments':class_assignments}
        finally:
            conn.close()


    @staticmethod
    def get_student_assignment_info(teacher_name, operand, json_result):
        account_name = operand.get('accountname', '')
        lab_code = operand.get('labcode', '')

        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "select lab.labname, lab.guides, grade, remark, not isnull(submittime) as submitted, not isnull(reviewtime) as reviewed \
                    from assignment inner join lab on assignment.labcode=lab.code \
                    where assignment.accountname='{}' and lab.code='{}'".format(account_name, lab_code)
                cursor.execute(sql)
                result = cursor.fetchone()
                json_result.data = result
        finally:
            conn.close()
    

    # 确保实验对应的容器启动
    @staticmethod
    def ensure_lab_container(teacher_name, operand, json_result):
        lab_code = operand.get('labcode', '')
        labs_dir = PathBuilder.get_students_dir()            # 容器仅装载到/data/students/目录即可

        # 获取实验对应的容器镜像名称
        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = 'select b.imagename from course as a inner join image as b on a.imageid=b.imageid where a.code={}'
                sql_formatted = sql.format(lab_code[0:2])
                cursor.execute(sql_formatted)
                image_name = cursor.fetchone()['imagename']
        finally:
            conn.close()

        service_name = JupyterServiceUtil.build_jupyter_service_name(teacher_name, image_name)      # 根据教师的用户名来生成容服务名称
        image_full_name = JupyterServiceUtil.get_image_full_name(image_name)         
        json_result.data = service_name

        user_jupyter_service_name, _ = JupyterServiceUtil.find_user_jupyter_services(teacher_name)
        if user_jupyter_service_name is not None:   # 该教师已经使用了某个jupyter容器实例
            # 检查原有的jupyter服务是否与当前所需的容器一致
            if user_jupyter_service_name == service_name:       # 一致，无需销毁原有容器实例，也不必创建新容器实例
                pass
            else:                                               # 不一致，删除原有的Service
                JupyterServiceUtil.remove_service(user_jupyter_service_name)    
                # 创建jupyter容器服务
                json_result.success = JupyterServiceUtil.create_service(service_name, image_full_name, labs_dir)
        else:                                       # 该用户尚未使用任何jupyter容器实例
            json_result.success = JupyterServiceUtil.create_service(service_name, image_full_name, labs_dir)

        if not json_result.success:
            json_result.message = '容器创建错误'


    # 检查jupyer notebook server是否已经就绪
    @staticmethod
    def check_notebook_ready(teacher_name, operand, json_result):
        service_name = operand.get('servicename', '')
        account_name = operand.get('accountname', '')
        _, port = JupyterServiceUtil.get_service_state_and_port(service_name)
        test_url = "http://{}:{}/notebooks".format(tornado.options.options.docker_swarm_manager_ip,port)
        jupyter_url = test_url + "/{}/{}/".format(account_name, tornado.options.options.data_student_lab)
        req = request.Request(test_url)
        try:
            res = request.urlopen(req)
            res.close()
            json_result.data = jupyter_url
        except:
            json_result.data = ''

    
    # 提交作业评分和评价
    @staticmethod
    def grade_assignment(teacher_name, operand, json_result):
        account_name = operand.get('accountname', '')
        lab_code = operand.get('labcode', '')
        grade = operand.get('grade', 0)
        remark = operand.get('remark', '')
        current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))

        conn = MySQLConnection.get_connection()
        try:
            with conn.cursor() as cursor:
                sql = "update assignment set grade={}, remark='{}', reviewtime='{}' where accountname='{}' and labcode='{}'".format(
                    grade, remark, current_time, account_name, lab_code
                )
                cursor.execute(sql)
                conn.commit()
        except:
            json_result.success = False
            json_result.message = "提交评分出错"
        finally:
            conn.close()


dispatch_map = {
    'get_class_assignments' : AssignmentHandler.get_class_assignments,
    'get_student_assignment_info' : AssignmentHandler.get_student_assignment_info,
    'ensure_lab_container' : AssignmentHandler.ensure_lab_container,
    'check_notebook_ready' : AssignmentHandler.check_notebook_ready,
    'grade_assignment' : AssignmentHandler.grade_assignment
}

default_handlers = [
    (r'/class_assignments', AssignmentHandler),
]